# coding=utf-8

# establish connection

from ui import ConfigWindow
from alg import alg_process

import os
import random
import json
import time

# http服务器的端口和websockets监听的端口。
httpPort = '9580'
wsPort = '9575'

#win = ConfigWindow()
#win.mainloop()


# 关掉占用了wsPort的进程
def killport(port):
    command = "kill -9 $(netstat -nlp | grep :" + str(port) + " | awk '{print $7}' | awk -F'/' '{{ print $1 }}')"
    os.system(command)


if os.name == 'posix':
    killport(wsPort)
    killport(httpPort)

# 获取服务端IP地址，windows系统使用socket直接读取，linux系统使用os调用ip addr命令，并用grep和awk过滤提取显示的IP地址
localIp = []
if os.name == 'nt':
    import socket

    localIp.append(socket.gethostbyname(socket.gethostname()))
elif os.name == 'posix':
    IPs = os.popen("ip addr | grep 'inet '|awk '{print $2}'|awk -F'/' '{print $1}'").readlines()
    for ip in IPs:
        if not '127.0.0.1' in ip:
            localIp.append(ip.strip('\n'))

import asyncio
import websockets
import workmode


# 记录服务器运行状态，等待waiting，运行running，暂停paused，停止stopped
serverStatus = {'stat': 'waiting', 'command': '', 'msg': ''}
allLinks = []
datanow = {}

# socket服务器向前台发送最简单的消息，
# 默认为text，处理方式为直接显示
# para，会根据value的内容改变前台的参数
# btr，发送电量和是否充电的状态
# data，idvt，转移曲线和电流随时间变化的曲线
# clean,会清楚消息框的内容，然后显示消息
async def sendmessage(msg, type='text'):
    resp = {}
    resp["type"] = type
    resp["value"] = msg
    for link in allLinks:
        try:
            await link.send(json.dumps(resp))
        except:
            if link in allLinks:
                allLinks.remove(link)
workmode.sendmessage = sendmessage


async def sendonly(websocket, msg, type='text'):
    resp = {}
    resp["type"] = type
    resp["value"] = msg
    try:
        await websocket.send(json.dumps(resp))
    except:
        if websocket in allLinks:
            allLinks.remove(websocket)

##############数据导出######################################
saveContent = ''

import matplotlib.pyplot as plt
import numpy as np
import re

#导出图片.tif
def draw_curve(file_name, x_label='Time(s)'):
    lines = [i for i in saveContent.split('\n') if '-------' not in i if '..' not in i if ':' not in i]
    data = []
    for i in lines:
        if re.match(r'^(\-|\d)+', i) is not None:
            tmp = i.split(' ')
            tmp[-1] = tmp[-1].replace('\n',',') #去掉换行符
            data.append(tmp)

    data = np.array(data)
    # print(data.shape)
    # print(data)
    data = data[:, 0:-1].astype(np.float32)
    # print(data)
    x_axis = data[:, 0]
    #y_axis = data[:, 1]
    adxy = np.array(data)
    # (x_axis, y_axis) = read_data(file_name)
    plt.figure(figsize=(8, 6))  # 确定图的大小
    plt.xlabel(x_label)  # 横坐标名称
    plt.ylabel("Current(uA)")  # 纵坐标名称
    # plt.title("Example")    # 图表名称
    if x_label == 'Time(s)':
       x_axis = x_axis/1000
       for nn in range(1,adxy.shape[1]-5):       
          y_axis = data[:,nn]      
          plt.plot(x_axis, y_axis)
          
       plt.savefig('.'+file_name.split('.')[1] + '.tif')
    else:   
       for nn in range(1,adxy.shape[1]):       
          y_axis = data[:,nn]      
          plt.plot(x_axis, y_axis)
          
    plt.savefig('.'+file_name.split('.')[1] + '.tif')
    plt.close()

#导出数据.txt
def saveData(log):
    from datetime import datetime
    now = datetime.now()
    time_string = now.strftime(r"%Y-%m-%d_%H-%M-%S")

    fn = './data/' + time_string + '.txt'
    f = open(fn, 'w')
    f.write(log + '------------------------------\n' + saveContent)
    f.close()
    draw_curve(fn, curve_xlabel)
    return fn + ' 已保存'
###################################################################

curve_xlabel = ''

class starT():
    def __init__(self):
        self.isRunning = False
        self.start = None
        self.total = 1
        self.endAlert = ""
        self.name = ""
    def reset(self):
        self.__init__()
starTimer = starT()

# 执行模块，根据serverStatus的修改进行不同的操作
# 执行模块与控制模块异步执行，同时运行，通过serverStatus互相通讯实现实时控制。
async def mainthread():
    print("main thread working!")
    import asyncio

    # global解决外部变量函数内无法读取的问题
    global serverStatus
    global curve_xlabel

    while True:
        message = ''
        # 如果serverStatus.stat处于waiting状态则无限循环
        # 一旦被修改为running则会根据msg的类型执行操作
        while True:
            await asyncio.sleep(0.05)
            if serverStatus['stat'] == 'running':
                message = serverStatus['command']
                break
                # 功能选择
        starTimer.reset()
        if message in workmode.allmode.keys():
            global saveContent
            saveContent = ''
            datanow['datas'] = []
            wMode = workmode.allmode[message]
            if message == '#trans':
                curve_xlabel = 'Vg(V)'
            elif message == '#idvt':
                curve_xlabel = 'Time(s)'
            await wMode.prepare()
            datanow['echarts'] = wMode.eChartsSet
            datanow['xcounts'] = wMode.xCounts
            
            await sendmessage(wMode.eChartsSet, 'echarts')
            await sendmessage(wMode.xCounts, 'xcounts')
            for ip in localIp:
                await sendmessage(ip + ':' + httpPort)
            datanow['stat'] = "Mode:"+message+",running..."
            
            while wMode.isContinue() and True:
                # 监测到stat变为stopped则停止循环，并且修改stat为waiting
                if serverStatus['stat'] == 'stopped':
                    serverStatus['stat'] = 'waiting'
                    break
                if serverStatus['command'] == 'tare' and message == '#idvt':
                    tareI = await wMode.loopBody(n='tare')
                    saveContent += '---------------tare:' + tareI + '------------------\n'
                    serverStatus['command'] = ''
                if False and serverStatus['command'] == 'timer':
                    m = serverStatus['msg']
                    starTimer.isRunning = True
                    starTimer.start = time.time()
                    starTimer.total = m["total"]
                    starTimer.endAlert = m["endcommand"]
                    starTimer.name = m["name"]
                    serverStatus['command'] = ''
                if starTimer.isRunning:
                    progBar = int((time.time()-starTimer.start)*100/float(starTimer.total))
                    if progBar > 100:
                        progBar = 100
                    await sendmessage({
                        "name":starTimer.name,
                        "value":str(progBar)+r"%"
                    },'bar')
                    if progBar == 100:
                        await sendmessage("",starTimer.endAlert)
                        starTimer.reset()
                # 发送json形式的数据，如果是测试环境，产生的是0-1之间的随机数。
                resp = {}
                resp["type"] = "data"
                resp["value"] = await wMode.loopBody()
                listValue = eval(resp["value"])
                datanow['datas'].append(resp)
                while(len(datanow['datas'])>600):
                    datanow['datas'].pop(0)
                #for val in listValue:
                #    saveContent += '{} '.format(val)
                #saveContent += '\n'
                await sendmessage(resp["value"],'data')
                
                wMode.afterLoop()

                # stat被修改为paused时，进入循环等待，直到stat被修改为running或者stopped
                if serverStatus['stat'] == 'paused':
                    datanow['stat'] = "Mode:" + message + ", Paused."
                    while True:
                        await asyncio.sleep(0.05)
                        if serverStatus['stat'] == 'running':
                            break
                        if serverStatus['stat'] == 'stopped':
                            serverStatus['stat'] == 'waiting'
                            break
            await wMode.endLoop()
            datanow['stat'] = 'Waiting...'
            serverStatus['stat'] = 'waiting'
            await sendmessage({
                "name":"准备阶段",
                "value":r"0%"
            },'bar')
            if message == '#idvt':
                # --------------------2023.2.9更改------------------------------ #
                # await sendmessage(alg_process(wMode.get_alg(), saveContent), "alert")
                try:
                    await sendmessage(alg_process(saveContent), "alert")
                except Exception as e:
                    print(e)
                # --------------------2023.2.9更改------------------------------ #
            # await sendmessage( saveData(serverStatus['msg']))
            serverStatus['msg'] = ''
            starTimer.reset()

        else:
            await sendmessage(message)
            serverStatus['stat'] = 'waiting'



# 控制模块，与执行模块一起运行。
# 主要负责接收前台发送的命令，并进行处理。
# 以修改serverStatus的方式控制执行模块的运行方式。
async def controlthread(websocket):
    global serverStatus
    while True:
        try:
            req = json.loads(await websocket.recv())
        except:
            if websocket in allLinks:
                allLinks.remove(websocket)
            break
        message = req['command']
        if message == '#stop':
            serverStatus['stat'] = 'stopped'
            serverStatus['command'] = ''
            serverStatus['msg'] = req['msg']
        elif message == '#pause':
            serverStatus['stat'] = 'paused'
            serverStatus['command'] = ''
            serverStatus['msg'] = ''
        elif message == '#continue':
            serverStatus['stat'] = 'running'
            serverStatus['command'] = ''
            serverStatus['msg'] = ''
        elif message == '#tare':
            serverStatus['command'] = 'tare'
            serverStatus['msg'] = ''
        elif message == '#save':
            await sendmessage(saveData(req['msg']))
        elif message == '#model':
            with open("temp.json","w") as f:
                f.write(json.dumps(json.loads(req['msg']),indent=4,ensure_ascii=False))
            await sendmessage("模型设置更新成功，请刷新界面")
        # 设置参数：将参数设置为前端传来的参数数据
        elif message == '#set':
            para = req['msg']
            workmode.modeConfig('set', eval(para))
            workmode.savepara()
            await sendmessage('参数保存成功！')
        # 回复默认参数，并把默认参数传回前端
        elif message == '#default':
            workmode.modeConfig('default')
            await sendmessage(workmode.modeConfig(), 'para')
            workmode.savepara()
            await sendmessage('恢复默认参数！')
        else:
            serverStatus['stat'] = 'running'
            serverStatus['command'] = message
            serverStatus['msg'] = req['msg']



# 基本的回调函数，通过传递websocket实现各种通讯功能。
async def echo(websocket, path):
    # 连接成功后发送IP，以及当前保存的参数。
    print("link[{}] start!".format(id(websocket)))
    allLinks.append(websocket)
    for ip in localIp:
        await sendonly(websocket, ip + ':' + httpPort)
    if workmode.envTest:
        await sendonly(websocket, '当前为测试环境！')
    workmode.loadpara()
    with open("temp.json","r",encoding="utf-8") as f:
        await sendonly(websocket,json.loads(f.read()),'model')
    for x in ['xcounts','echarts']:
        if x in datanow:
            await sendonly(websocket,datanow[x],x)
    if 'stat' in datanow:
        await sendonly(websocket, datanow['stat'])
    if 'datas' in datanow:
        for resp in datanow['datas']:
            await sendonly(websocket,resp["value"],'data')    
    await sendonly(websocket, workmode.modeConfig(), 'para')
    await controlthread(websocket)
    print("link[{}] terminated!".format(id(websocket)))
    del websocket

# ws服务器实体
def wsServer():
    print('Data Collection Server Start!')
    print('---------------------------')

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    tasks = asyncio.gather(
        websockets.serve(echo, '0.0.0.0', wsPort),mainthread())
    loop.run_until_complete(tasks)
    loop.run_forever()

import bottle
from bottle import static_file
import os

rt = os.getcwd()

@bottle.route('/')
def starfile():
    return static_file('index.html', root=rt)


@bottle.route('/<path:path>')
def starfile(path):
    return static_file(path, root=rt)


#bottle-http服务器实体
def httpServer():
    bottle.run(server="paste",host='0.0.0.0', port=httpPort)

import threading
if __name__ == "__main__":
# 使用多线程让二者同时运行
    t1 = threading.Thread(target=wsServer)
    t2 = threading.Thread(target=httpServer)
    t2.start()
    t1.start()
    t1.join()
    t2.join()
